﻿using System;
using System.Linq;
using System.Windows;
using HandyControl.Data;

namespace HandyControl.Controls;

public class SideMenu : HeaderedSimpleItemsControl
{
    private SideMenuItem _selectedItem;

    private SideMenuItem _selectedHeader;

    private bool _isItemSelected;

    public SideMenu()
    {
        AddHandler(SideMenuItem.SelectedEvent, new RoutedEventHandler(SideMenuItemSelected));

        Loaded += (s, e) => Init();
    }

    protected override void Refresh()
    {
        base.Refresh();

        Init();
    }

    private void Init()
    {
        if (ItemsHost == null) return;

        OnExpandModeChanged(ExpandMode);
    }

    private void SideMenuItemSelected(object sender, RoutedEventArgs e)
    {
        if (e.OriginalSource is SideMenuItem item)
        {
            if (item.Role == SideMenuItemRole.Item)
            {
                _isItemSelected = true;

                if (Equals(item, _selectedItem)) return;

                if (_selectedItem != null)
                {
                    _selectedItem.IsSelected = false;
                }

                _selectedItem = item;
                _selectedItem.IsSelected = true;
                RaiseEvent(new FunctionEventArgs<object>(SelectionChangedEvent, this)
                {
                    Info = e.OriginalSource
                });
            }
            else
            {
                if (!Equals(item, _selectedHeader))
                {
                    if (_selectedHeader != null)
                    {
                        if (ExpandMode == ExpandMode.Freedom && item.ItemsHost.IsVisible && !_isItemSelected)
                        {
                            item.IsSelected = false;
                            SwitchPanelArea(item);
                            return;
                        }

                        _selectedHeader.IsSelected = false;
                        if (ExpandMode != ExpandMode.Freedom)
                        {
                            SwitchPanelArea(_selectedHeader);
                        }
                    }

                    _selectedHeader = item;
                    _selectedHeader.IsSelected = true;
                    SwitchPanelArea(_selectedHeader);
                }
                else if (ExpandMode == ExpandMode.Freedom && !_isItemSelected)
                {
                    _selectedHeader.IsSelected = false;
                    SwitchPanelArea(_selectedHeader);
                    _selectedHeader = null;
                }

                if (_isItemSelected)
                {
                    _isItemSelected = false;
                }
                else if (_selectedHeader != null)
                {
                    if (AutoSelect)
                    {
                        if (_selectedItem != null)
                        {
                            _selectedItem.IsSelected = false;
                            _selectedItem = null;
                        }

                        _selectedHeader.SelectDefaultItem();
                    }
                    _isItemSelected = false;
                }

                if (!item.HasItems)
                {
                    RaiseEvent(new FunctionEventArgs<object>(SelectionChangedEvent, this)
                    {
                        Info = e.OriginalSource
                    });
                }
            }
        }
    }

    private void SwitchPanelArea(SideMenuItem oldItem)
    {
        switch (ExpandMode)
        {
            case ExpandMode.ShowAll:
                return;
            case ExpandMode.ShowOne:
            case ExpandMode.Freedom:
            case ExpandMode.Accordion:
                oldItem.SwitchPanelArea(oldItem.IsSelected);
                break;
        }
    }

    protected override DependencyObject GetContainerForItemOverride() => new SideMenuItem();

    protected override bool IsItemItsOwnContainerOverride(object item) => item is SideMenuItem;

    public static readonly DependencyProperty AutoSelectProperty = DependencyProperty.Register(
        nameof(AutoSelect), typeof(bool), typeof(SideMenu), new PropertyMetadata(ValueBoxes.TrueBox));

    public bool AutoSelect
    {
        get => (bool) GetValue(AutoSelectProperty);
        set => SetValue(AutoSelectProperty, ValueBoxes.BooleanBox(value));
    }

    public static readonly DependencyProperty ExpandModeProperty = DependencyProperty.Register(
        nameof(ExpandMode), typeof(ExpandMode), typeof(SideMenu), new PropertyMetadata(default(ExpandMode), OnExpandModeChanged));

    private static void OnExpandModeChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
    {
        var ctl = (SideMenu) d;
        var v = (ExpandMode) e.NewValue;

        if (ctl.ItemsHost == null)
        {
            return;
        }

        ctl.OnExpandModeChanged(v);
    }

    private void OnExpandModeChanged(ExpandMode mode)
    {
        if (mode == ExpandMode.ShowAll)
        {
            ShowAll();
        }
        else if (mode == ExpandMode.ShowOne)
        {
            SideMenuItem sideMenuItemSelected = null;
            foreach (var sideMenuItem in ItemsHost.Children.OfType<SideMenuItem>())
            {
                if (sideMenuItemSelected != null)
                {
                    sideMenuItem.IsSelected = false;
                    if (sideMenuItem.ItemsHost != null)
                    {
                        foreach (var sideMenuSubItem in sideMenuItem.ItemsHost.Children.OfType<SideMenuItem>())
                        {
                            sideMenuSubItem.IsSelected = false;
                        }
                    }
                }
                else if (sideMenuItem.IsSelected)
                {
                    switch (sideMenuItem.Role)
                    {
                        case SideMenuItemRole.Header:
                            _selectedHeader = sideMenuItem;
                            break;
                        case SideMenuItemRole.Item:
                            _selectedItem = sideMenuItem;
                            break;
                    }

                    ShowSelectedOne(sideMenuItem);
                    sideMenuItemSelected = sideMenuItem;

                    if (sideMenuItem.ItemsHost != null)
                    {
                        foreach (var sideMenuSubItem in sideMenuItem.ItemsHost.Children.OfType<SideMenuItem>())
                        {
                            if (_selectedItem != null)
                            {
                                sideMenuSubItem.IsSelected = false;
                            }
                            else if (sideMenuSubItem.IsSelected)
                            {
                                _selectedItem = sideMenuSubItem;
                            }
                        }
                    }
                }
            }
        }
    }

    public ExpandMode ExpandMode
    {
        get => (ExpandMode) GetValue(ExpandModeProperty);
        set => SetValue(ExpandModeProperty, value);
    }

    public static readonly DependencyProperty PanelAreaLengthProperty = DependencyProperty.Register(
        nameof(PanelAreaLength), typeof(double), typeof(SideMenu), new PropertyMetadata(double.NaN));

    public double PanelAreaLength
    {
        get => (double) GetValue(PanelAreaLengthProperty);
        set => SetValue(PanelAreaLengthProperty, value);
    }

    private void ShowAll()
    {
        foreach (var sideMenuItem in ItemsHost.Children.OfType<SideMenuItem>())
        {
            sideMenuItem.SwitchPanelArea(true);
        }
    }

    private void ShowSelectedOne(SideMenuItem item)
    {
        foreach (var sideMenuItem in ItemsHost.Children.OfType<SideMenuItem>())
        {
            sideMenuItem.SwitchPanelArea(Equals(sideMenuItem, item));
        }
    }

    public static readonly RoutedEvent SelectionChangedEvent = EventManager.RegisterRoutedEvent(
        "SelectionChanged", RoutingStrategy.Bubble, typeof(EventHandler<FunctionEventArgs<object>>), typeof(SideMenu));

    public event EventHandler<FunctionEventArgs<object>> SelectionChanged
    {
        add => AddHandler(SelectionChangedEvent, value);
        remove => RemoveHandler(SelectionChangedEvent, value);
    }
}
